/*!
* @file TestData.cpp
* @brief 数据结构测试
* @date 2024/9/27
*/

#include <gtest/gtest.h>
#include "Vector3D.h"
#include "BoundBox3D.h"
#include "Matrix4D.h"
#include "Axis.h"
#include "Rotation.h"
#include "Placement.h"
#include "Coordinate.h"
#include "Color.h"

using namespace data;

/**
* @brief    数据结构验证组
*/
class TestData : public ::testing::Test
{
public:
    static void SetUpTestSuite()
    {
    }
    static void TearDownTestSuite()
    {
    }
};

/**
* @brief    二维矢量测试
*/
TEST_F(TestData, Vector2)
{
    Vector2d vec1;
    Vector2d vec2(1.0, 1.0);
    EXPECT_NE(vec1, vec2);

    Vector2d vec3(vec1);
    EXPECT_EQ(vec1, vec3);

    vec3 = vec2;
    EXPECT_NE(vec1, vec3);
    EXPECT_EQ(vec2, vec3);

    Vector2f vec4(1.0, 1.0);
    Vector2d vec5(vec4);
    Vector2d vec6 = vec2 + vec5;
    EXPECT_EQ(vec6, Vector2d(2.0, 2.0));
    EXPECT_DOUBLE_EQ(vec6.Angle(), D_PI / 4);

    Vector2d vec7 = vec6 - Vector2d(-1.0, -2.0);
    EXPECT_DOUBLE_EQ(vec7.Length(), 5.0);
    EXPECT_DOUBLE_EQ(vec7.Distance(vec6), Vector2d(1.0, 2.0).Length());

    vec7 *= 2.0;
    EXPECT_DOUBLE_EQ(vec7.Length(), 10.0);

    vec7 /= 4.0;
    EXPECT_DOUBLE_EQ(vec7.Length(), 2.5);

    Vector2d vec8 = vec7;
    vec8.Rotate(D_PI / 4);
    EXPECT_DOUBLE_EQ(vec8.Length(), vec7.Length());
    EXPECT_DOUBLE_EQ(vec8.Angle(), vec7.Angle() + D_PI / 4);
    EXPECT_DOUBLE_EQ(vec8.GetAngle(vec7), D_PI / 4);
}

/**
* @brief    三维矢量测试
*/
TEST_F(TestData, Vector3)
{
    Vector3d vec1;
    Vector3d vec2(1.0, 0.0, 0.0);
    EXPECT_NE(vec1, vec2);
    EXPECT_FALSE(vec1.IsEqual(vec2, 0));
    EXPECT_EQ(vec2.Vec2(), Vector2d(1.0, 0.0));
    EXPECT_DOUBLE_EQ(vec2.Length(), 1.0);

    Vector3d vec3(vec1);
    EXPECT_EQ(vec1, vec3);

    vec3 = vec2;
    EXPECT_NE(vec1, vec3);
    EXPECT_EQ(vec2, vec3);

    Vector3f vec4(0.0, 1.0, 0.0);
    Vector3d vec5(vec4);
    Vector3d vec6 = vec2 + vec5;
    EXPECT_EQ(vec6, Vector3d(1.0, 1.0, 0.0));

    double angle = vec2.GetAngle(vec4);
    EXPECT_DOUBLE_EQ(angle, D_PI / 2);
    EXPECT_TRUE(vec2.IsNormal(vec4, 0));

    double dot = vec2.Dot(vec4);
    EXPECT_DOUBLE_EQ(dot, 0.0);

    Vector3d cross = vec2.Cross(vec4);
    EXPECT_EQ(cross, Vector3d(0.0, 0.0, 1.0));

    Vector3d point(1, 1, 1);
    Vector3d project = point.ProjectToLine(Vector3d(0, 0, 0), Vector3d(1, 0, 0));
    EXPECT_EQ(Vector3d(1, 0, 0), project);
    EXPECT_DOUBLE_EQ(1, point.DistanceToLine(Vector3d(0, 0, 0), Vector3d(1, 1, 0)));

    project = point.ProjectToPlane(Vector3d(0, 0, 0), Vector3d(0, 0, 1));
    EXPECT_EQ(Vector3d(1, 1, 0), project);
    EXPECT_DOUBLE_EQ(1, point.DistanceToPlane(Vector3d(0, 0, 0), Vector3d(0, 0, 1)));
}

/**
* @brief    二维包围盒测试
*/
TEST_F(TestData, BoundBox2D)
{
    BoundBox2d box1;
    EXPECT_FALSE(box1.IsValid());

    box1.Add(Vector2d(1, 1));
    EXPECT_TRUE(box1.IsValid());

    box1.Add(Vector2d(-1, -1));
    EXPECT_TRUE(box1.IsValid());
    EXPECT_EQ(Vector2d(0, 0), box1.Center());
    EXPECT_TRUE(box1.Contains(Vector2d(0.5, 0.5)));
    EXPECT_TRUE(box1.Contains(Vector2d(1, -1)));
    EXPECT_FALSE(box1.Contains(Vector2d(2, 0)));

    BoundBox2d box2(1, 1, 2, 2);
    EXPECT_FALSE(box1.Intersect(box2));
    box1.Add(box2);
    EXPECT_FALSE(box1.IsEqual(box2, 0));
    box2.Add(box1);
    EXPECT_TRUE(box1.IsEqual(box2, 0));
}

/**
* @brief    三维包围盒测试
*/
TEST_F(TestData, BoundBox3D)
{
    BoundBox3d box1;
    EXPECT_FALSE(box1.IsValid());

    box1.Add(Vector3d(1, 1, 1));
    EXPECT_TRUE(box1.IsValid());

    box1.Add(Vector3d(-1, -1, -1));
    EXPECT_TRUE(box1.IsValid());
    EXPECT_EQ(Vector3d(0, 0, 0), box1.Center());
    EXPECT_TRUE(box1.Contains(Vector3d(0.5, 0.5, 0.5)));
    EXPECT_TRUE(box1.Contains(Vector3d(1, 1, -1)));
    EXPECT_FALSE(box1.Contains(Vector3d(2, 0, 0)));

    BoundBox3d box2(Vector3d(1, 1, 1), 1);
    EXPECT_TRUE(box1.Intersect(box2));
    box1.Add(box2);
    EXPECT_FALSE(box1.IsEqual(box2, 0));
    box2.Add(box1);
    EXPECT_TRUE(box1.IsEqual(box2, 0));

    EXPECT_TRUE(box2.Intersect(Vector3d(2, 2, 2), Vector3d(1, 1, -1)));
    box2.Shrink(0.001);
    EXPECT_FALSE(box2.Intersect(Vector3d(2, 2, 2), Vector3d(1, 1, -1)));
}

/**
* @brief    旋转量测试
*/
TEST_F(TestData, Rotation)
{
    Rotation rot1;
    EXPECT_TRUE(rot1.IsIdentity());
    EXPECT_TRUE(rot1.IsIdentity(0));

    Vector3d axis;
    double angle;
    rot1.Get(axis, angle);
    EXPECT_EQ(Vector3d(0, 0, 1), axis);
    EXPECT_DOUBLE_EQ(0, angle);

    Rotation rot2(Vector3d(1, 0, 0), Vector3d(0, 1, 0));
    rot2.Get(axis, angle);
    EXPECT_EQ(Vector3d(0, 0, 1), axis);
    EXPECT_DOUBLE_EQ(D_PI / 2, angle);

    Vector3d vec(1, 1, 0);
    vec = rot2.MultVec(vec);
    EXPECT_EQ(Vector3d(-1, 1, 0), vec);

    Rotation rot3;
    rot3.Set(axis, angle);
    EXPECT_EQ(rot2, rot3);

    Rotation rot4 = rot2 * rot3;
    rot4.Get(axis, angle);
    EXPECT_EQ(Vector3d(0, 0, 1), axis);
    EXPECT_DOUBLE_EQ(D_PI, angle);
}

/**
* @brief    定位量测试
*/
TEST_F(TestData, Placement)
{
    Placement pla1;
    EXPECT_EQ(Vector3d(0, 0, 0), pla1.position);
    EXPECT_TRUE(pla1.rotation.IsIdentity());
    EXPECT_TRUE(pla1.IsIdentity());
    EXPECT_TRUE(pla1.IsIdentity(0));

    pla1.Move(Vector3d(1, 1, 1));
    EXPECT_EQ(Vector3d(1, 1, 1), pla1.position);

    Vector3d vec(1, 0, 0);
    EXPECT_EQ(Vector3d(2, 1, 1), vec * pla1);

    pla1.rotation = Rotation(Vector3d(0, 0, 1), D_PI / 2);
    EXPECT_EQ(Vector3d(1, 2, 1), vec * pla1);

    Matrix4d matrix = pla1.ToMatrix();
    Vector3d v = matrix.MultVec(vec);
    Vector3d d = matrix.MultDir(vec);
    EXPECT_EQ(Vector3d(1, 2, 1), v);
    EXPECT_EQ(Vector3d(0, 1, 0), d);

    Placement pla2(Vector3d(0, 1, 0), Rotation(Vector3d(0, 0, 1), D_PI / 4));
    Placement pla3 = pla1 * pla2;
    EXPECT_EQ(Vector3d(0, 1, 1), pla3.position);
}

/**
* @brief    坐标系测试
*/
TEST_F(TestData, Coordinate)
{
    Coordinate coord;
    coord.SetPosition(Vector3d(1, 1, 1));
    coord.SetAxes(Vector3d(0, 0, 1), Vector3d(0, 1, 0));
    EXPECT_EQ(Vector3d(0, 1, 0), coord.X());
    EXPECT_EQ(Vector3d(-1, 0, 0), coord.Y());
    EXPECT_EQ(Vector3d(0, 0, 1), coord.Z());

    Vector3d point(0, 1, 1);
    Vector3d local = coord.ToLocalPoint(point);
    EXPECT_EQ(Vector3d(0, 1, 0), local);
    EXPECT_EQ(point, coord.ToWorldPoint(local));
}

/**
* @brief    矩阵测试
*/
TEST_F(TestData, Matrix)
{
    Matrix4d matrix1;
    matrix1.SetTranslate(Vector3d(1, 1, 1));
    EXPECT_EQ(Vector3d(1, 1, 1), matrix1.MultVec(Vector3d(0, 0, 0)));

    Matrix4d matrix2;
    matrix2.SetRotate(Vector3d(0, 0, 0), Vector3d(0, 0, 1), D_PI / 2);
    EXPECT_TRUE(Vector3d(-1, 1, 1).IsEqual(matrix2.MultVec(Vector3d(1, 1, 1)), 0.001));

    Matrix4d matrix3;
    matrix3.SetMirror(Vector3d(0, 0, 0), Vector3d(1, 0, 0));
    EXPECT_TRUE(Vector3d(-1, 1, 1).IsEqual(matrix3.MultVec(Vector3d(1, 1, 1)), 0.001));

    Matrix4d matrix4 = matrix1 * matrix2 * matrix3;
    EXPECT_TRUE(Vector3d(2, 2, 2).IsEqual(matrix4.MultVec(Vector3d(1, 1, 1)), 0.001));
}
